﻿using System;
using System.Threading;
using System.Runtime.InteropServices;

namespace Alex.Library.Tasks
{
    /// <summary>
    /// 作为任务类的基类
    /// </summary>
    public abstract class TaskBase : ITask
    {
        #region Ctor

        /// <summary>
        /// 构造函数
        /// </summary>
        public TaskBase()
        {
            this.Timeout = TaskTimeout.Forever;            
            this.CanRepeat = true;
            this.HasError = false;
            this.ErrorInfo = String.Empty;
            this.State = TaskState.None;
            this.Runtimes = TimeSpan.Zero;
        }

        #endregion

        #region Properties

        /// <summary>
        /// 任务标识，常用GUID
        /// </summary>
        public abstract string ID { get; }

        /// <summary>
        /// 任务描述
        /// </summary>
        public abstract string Description { get; }

        /// <summary>
        /// 是否是异步任务
        /// </summary>
        public abstract bool IsAsync { get; }

        /// <summary>
        /// 获取或设置任务执行的超时时间(单位：毫秒)，如果不设置超时时间则默认为TaskTimeout.Forever
        /// </summary>
        public double Timeout { get; set; }

        /// <summary>
        /// 获取任务状态
        /// </summary>
        public TaskState State { get; private set; }

        /// <summary>
        /// 获取是否有错误
        /// </summary>
        public bool HasError { get; private set; }

        /// <summary>
        /// 获取错误信息
        /// </summary>
        public string ErrorInfo { get; private set; }

        /// <summary>
        /// 获取或设置任务是否允许重复执行
        /// </summary>
        public bool CanRepeat { get; set; }

        /// <summary>
        /// 获取累计执行时长
        /// </summary>
        public TimeSpan Runtimes { get; private set; }

        #endregion

        #region Variables

        //计算任务累计执行时长的线程
        private Thread timer;
        //工作线程
        private Thread workThread;
        //线程同步操作信号量
        private ManualResetEvent allDone;

        #endregion

        #region Methods

        /// <summary>
        /// 需要处理的工作，方法中必须对State和HasError进行判断，
        /// 只有当状态为TaskState.Doing并且没有错误的情况下才能继续执行
        /// </summary>
        protected abstract void DoWork();

        /// <summary>
        /// 工作进度回滚
        /// </summary>
        protected abstract void RollbackWork();

        /// <summary>
        /// 工作完成
        /// </summary>
        protected abstract void CompleteWork();

        /// <summary>
        /// 执行任务
        /// </summary>
        /// <param name="rollbackIfError"></param>
        public virtual void Run(bool rollbackIfError)
        {
            if (State == TaskState.Doing)
            {
                throw new TaskBusyException();
            }

            if (!CanRepeat && State == TaskState.Completed)
            {
                throw new TaskRepeatException();
            }

            TaskManager.RegisterTask(this);

            this.State = TaskState.Doing;

            DateTime startTime = DateTime.Now;

            try
            {
                timer = new Thread(new ParameterizedThreadStart(RuntimesCallback));
                timer.Start(startTime);
                if (!IsAsync)
                {
                    allDone = new ManualResetEvent(false);
                    allDone.Reset();
                }
                workThread = new Thread(new ParameterizedThreadStart(ExecuteDoWork));
                workThread.Start(rollbackIfError);
                if (!IsAsync)
                {
                    allDone.WaitOne();
                }
            }
            catch (Exception ex)
            {
                DealError(ex);
                if (rollbackIfError)
                {
                    this.RollbackWork();
                }
            }
        }

        /// <summary>
        /// 取消任务
        /// </summary>
        /// <param name="needRollback"></param>
        public virtual void Cancel(bool needRollback)
        {
            if (IsAsync && workThread != null && workThread.IsAlive)
            {
                //workThread.Abort();
                //workThread.Join();
                this.State = TaskState.Canceled;
                if (needRollback)
                {
                    this.RollbackWork();
                }
            }
        }
        
        /// <summary>
        /// 处理错误异常
        /// </summary>
        /// <param name="ex"></param>
        protected virtual void DealError(Exception ex)
        {            
            this.HasError = true;
            this.ErrorInfo = ex.Message;            
        }        

        /// <summary>
        /// 工作线程回调函数(执行DoWork方法)
        /// </summary>
        /// <param name="obj"></param>
        private void ExecuteDoWork(object obj)
        {
            bool rollbackIfError = (bool)obj;
            try
            {
                DoWork();
                if (this.HasError && rollbackIfError)
                {
                    this.RollbackWork();
                }
                
                this.State = TaskState.Completed;
                //已完成，收尾工作
                this.CompleteWork();

                if (!IsAsync)
                {
                    allDone.Set();
                }
            }
            catch (Exception ex)
            {
                DealError(ex);
            }
        }

        /// <summary>
        /// 计算累计时长的线程回调函数
        /// </summary>
        /// <param name="obj"></param>
        private void RuntimesCallback(object obj)
        {
            while (!this.HasError && this.State == TaskState.Doing)
            {
                DateTime startTime = (DateTime)obj;
                this.Runtimes = DateTime.Now - startTime;
                if (this.Timeout != TaskTimeout.Forever)
                {
                    if (this.Runtimes.TotalMilliseconds > this.Timeout)
                    {
                        DealError(new TaskTimeoutException());
                        //workThread.Abort();
                        //workThread.Join();
                        if (!IsAsync)
                        {
                            allDone.Set();
                        }
                        this.State = TaskState.Completed;
                    }
                }
                Thread.Sleep(1);
            }
        }

        /// <summary>
        /// 释放任务所持有的资源
        /// </summary>
        public void Dispose()
        {
            
        }

        #endregion
    }
}
